#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <pthread.h>
#include <semaphore.h>
#include "timer.h"

#define DIM 10
#define NUM_OF_SHIPS 5

using namespace std;

//Global variables
int thread_count =2; //number of thread "players"
int thread_turn = 0; // 0 is thread 0 turn and 1 is thread 1 turn.
int **guess = new int* [2];
int whoWin = 0;
double start;
double endt;

int turn = 0;

pthread_mutex_t mut;
sem_t sem[2];

//Functions header
void printBoard(char matrix[DIM][DIM], long rank);
void play(char player[DIM][DIM], int* ships[NUM_OF_SHIPS], long rank);
void createThreads(pthread_t* thread_handles);
void* threadWork(void* rank);

int rand (int low, int high);
int allShipsDestroyed(int* ships[NUM_OF_SHIPS]);
int* setShip(int shipLenght, int x, int y, int direction, char matrix[DIM][DIM]);
int* setShoot(int coord[2], char matrix[DIM][DIM], int* ships[NUM_OF_SHIPS]);
int* getRandShoot();

int main(){
	srand(time(NULL));
    pthread_t* thread_handles;
    
    guess[0] = new int[2];
    guess[1] = new int[2];
    
    thread_handles = (pthread_t*) malloc(thread_count*sizeof(pthread_t));
    createThreads(thread_handles);

    
    cout << endl << "Time elapsed: " << endt - start << "s" << endl;
	return 0;
}

/* Print a board on the screen
 * @param matrix It's the board
 */
void printBoard(char matrix[DIM][DIM], long rank) {
	char row = 'A';
    
    cout << "Player: " << rank + 1<< endl;
	cout<<"  ";
	for(int j=0; j<DIM; j++) 
		cout << j+1<<"  ";
	
	cout<<endl;

	for(int i=0; i<DIM; i++) {
		cout << row;
		for(int j=0; j<DIM; j++) {
			cout <<" "<< matrix[i][j]<<" ";
		}
		cout << endl;
		row = (int)row+1;
	}
}

/* Set a specifc ship on the board
 * @param shipLenght Lengh of a ship to set on the board
 * @param x 		 Represents a row of the board
 * @param y 		 Represents a collum of the board
 * @param direction  Direction to set a ship (0..3)
 * @param matrix 	 It's the board
 * @param Return an array with coordinates of ships
 * the first element is the lenght of the array and 
 * all other elements are like 'rc' where r is the row
 * and c is the colum, so we need to divide the element by 
 * 10*Dim = row and get the modulus by 10*dim and get the column
 */
int* setShip(int shipLenght, int x, int y, int direction, char matrix[DIM][DIM]) {
	int i, j, flag;
    int *ship = new int[shipLenght+1];
    
    ship[0] = shipLenght;
    
	if(direction == 0) {//Direction is up
		//Check if a ship can be set
		if((x-shipLenght+1) <0 || matrix[x][y]!='~')	{
			//ship cannot be set because there is no space to set
            delete(ship);
			setShip(shipLenght, x, y, 1, matrix);
		}
		else {	
			flag = 0;
			//Check if there is anoter ship on the way
			for (i=x-shipLenght+1; i<=x; i++) {
				if(matrix[i][y] != '~') { 
					flag = 1;
					break;
				}
			}
			if(flag == 0) {
				//There is space to set a ship
				for (i=x-shipLenght+1, j=1; i<=x; i++, j++) {
					matrix[i][y] = (char) 48+shipLenght;
                    ship[j] = (10*DIM*i) + y;
				}
			}
			else {
				//There is no space to set a ship i this direction,
				//so try another one
                delete(ship);
				setShip(shipLenght, x, y, 1, matrix);
			}
		}
	}
	
	else if(direction == 1) { //Direction is left
		if((y-shipLenght+1) < 0 || matrix[x][y]!='~') {
            delete(ship);
			setShip(shipLenght, x, y, 2, matrix);
		}
		else {
			flag = 0;		
			for (j=y-shipLenght+1; j<=y; j++) {
				if(matrix[x][j] != '~') { 
					flag = 1;
					break;
				}
			}
			if(flag == 0) {
				for (j=y-shipLenght+1, i=1; j<=y; i++, j++) {
					matrix[x][j] = (char)48+shipLenght;
                    ship[i] = (10*DIM*(x)) + (j);
				}
			}
			else {
                delete(ship);
				setShip(shipLenght, x, y, 2, matrix);
			}
		}
	}

	else if(direction == 2) {//Direction is down
		if((x+shipLenght-1)>=DIM || matrix[x][y] != '~') {
            delete(ship);
			setShip(shipLenght, x, y, 3, matrix);
		}
		else {
			flag = 0;		
			for(i=x; i<= x+shipLenght-1; i++) {
				if(matrix[i][y] != '~') { 
					flag = 1;
					break;
				}
			}
			if(flag == 0) {
				for(i=x, j=1; i<= x+shipLenght-1; i++, j++) {
					matrix[i][y] =(char)48+shipLenght;
                    ship[j] = (10*DIM*(i))+(y);
				}
			}
			else {
                delete(ship);
				setShip(shipLenght, x, y, 3, matrix);
			}
		}
	}

	else if(direction == 3) { //Direction is right
		if((y+shipLenght-1)>=DIM || matrix[x][y] != '~'){
            delete(ship);
			setShip(shipLenght, rand(0, 9), rand(0, 9), 0, matrix);
		}
		else {
			flag = 0;		
			for(j=y; j<=y+shipLenght-1; j++) {
				if(matrix[x][j] != '~') { 
					flag = 1;
					break;
				}
			}
			if(flag == 0) {
				for(j=y, i=1; j<=y+shipLenght-1; i++, j++) {
					matrix[x][j] = (char)48+ shipLenght;
                    ship[i] = (10*DIM*(x))+(j);
				}
			}
			else {
                delete(ship);
				setShip(shipLenght, rand(0,9), rand(0,9), 0, matrix);
			}
		}
	}
    else ship = NULL;
    return ship;
}

int rand(int low, int high) {
	return (rand()%(high-low+1))+low;
}

int allShipsDestroyed(int* ships[NUM_OF_SHIPS]) {
    int result = 0;

    for (int i=0; i<NUM_OF_SHIPS; i++) {
        for (int j=1; j<=ships[i][0]; j++) {
            if(ships[i][j]>= 0) result = 1;
        }
    }
    if(result == 0) {
        return 1;
    }
    else return 0;
}

/* Set shoot in a matrix from a random shoot
 *
 */
int* setShoot(int coord[2], char matrix[DIM][DIM], int *ships[NUM_OF_SHIPS]) {
    int *shoot;
    shoot = new int[2];
    
    if (coord != NULL ) {
        if (matrix[coord[0]][coord[1]] == '~') {
            printf("Miss! -> Coord[%c,%d]\n", (char)(coord[0]+65), coord[1]+1);
            shoot[0] = coord[0];
            shoot[1] = coord[1];
        }
        
        else if (matrix[coord[0]][coord[1]] == 'x') {
            delete(shoot);
            coord = getRandShoot();
            setShoot(coord, matrix, ships);
        }
        else {
            printf("Hit! -> Coord[%c,%d]\n", (char)(coord[0]+65), coord[1]+1);
            shoot[0] = coord[0];
            shoot[1] = coord[1];
            for (int i=0; i<NUM_OF_SHIPS; i++) {
                for (int j=1; j<=ships[i][0]; j++) {
                    if((coord[0] == ships[i][j]/(10*DIM)) && (coord[1] == ships[i][j]%(10*DIM))) {
                        ships[i][j] = -1;
                    }
                }
            }
        }
        matrix[coord[0]][coord[1]] = 'x';
    }
    return shoot;
}

int* getRandShoot() {
    int* coord;
    coord = new int[2];

    coord[0] = rand(0,9);
    coord[1] = rand(0,9);

    return coord;
}

void* threadWork(void* rank) {
    int* ships[NUM_OF_SHIPS];
    char player[DIM][DIM];
    long myrank = (long) rank;
    
    //set watter in all matrix
    for (int i=0; i<DIM; i++){
        for(int j=0; j<DIM; j++){
            player[i][j] = '~';
            //player2[i][j] = '~';
            //player1Display[i][j] = '~';
            //player2Display[i][j] = '~';
        }
    }

    ships[0] = setShip(2, rand(0, 9), rand(0, 9), rand(0, 3), player);
    ships[1] = setShip(3, rand(0, 9), rand(0, 9), rand(0, 3), player);
    ships[2] = setShip(3, rand(0, 9), rand(0, 9), rand(0, 3), player);
    ships[3] = setShip(4, rand(0, 9), rand(0, 9), rand(0, 3), player);
    ships[4] = setShip(5, rand(0, 9), rand(0, 9), rand(0, 3), player);
    play(player, ships, myrank);
    return NULL;
}

void createThreads(pthread_t* thread_handles) {
    int thread;
    
    GET_TIME(start);
    pthread_mutex_init(&mut, NULL);
    for (thread = 0; thread < thread_count; thread++)
        pthread_create(&thread_handles[thread], NULL, threadWork, (void*)thread);
    
    for (thread = 0; thread < thread_count; thread++)
        pthread_join(thread_handles[thread], NULL);
    pthread_mutex_destroy(&mut);
    delete(thread_handles);
    GET_TIME(endt);
}

void play(char player[DIM][DIM], int* ships[NUM_OF_SHIPS], long rank) {
    int* coord = new int[2];
    
    int op = 0;
    while (op<100) {
        if(whoWin == 1) break;
        coord = getRandShoot();
    
        guess[(rank+1)%thread_count] = coord;
        pthread_mutex_lock(&mut);
        printf("-----------------Thread %ld attack Thread %ld------------\n", (rank+1)%thread_count+1, rank+1);
        setShoot(guess[rank], player, ships);
        printBoard(player, rank);
        pthread_mutex_unlock(&mut);

        if(allShipsDestroyed(ships) == 0) {
            whoWin = 1;
            printf("All ships destroyed! Thread %ld won!!\n", (rank+1)%thread_count+1);
            break;
        }
        
        op++;
    }
}